package com.jd.scaffold.dao.base.impl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.orm.ibatis.SqlMapClientCallback;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson.JSON;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.jd.scaffold.dao.base.BaseDao;
import com.jd.scaffold.common.entity.BaseEntity;
import com.jd.scaffold.common.entity.BasePageDTO;
import com.jd.scaffold.common.entity.Pagination;
import com.jd.scaffold.common.utils.ColumnUtil;
import com.jd.scaffold.common.utils.CurrencyUtils;
import com.jd.scaffold.domain.SysSequence;


/**
 * 
 * 描述:数据操作通用类，其他数据操作类应均继承与此类
 * <p>创建人：jrzhangwei 创建日期：2014年7月10日 </p>
 * @version V1.0
 */
public class BaseDaoImpl extends SqlMapClientTemplate implements BaseDao{
	
    public final static Logger log = LogManager.getLogger(BaseDaoImpl.class);
    
    @Resource(name="sqlMapClient")
    public void setSqlMapClientForAutowire(SqlMapClient sqlMapClient) {
        super.setSqlMapClient(sqlMapClient);
    }
    
    private static final String pagenationStatementSuffix = "Mapper.getPagenationList";
    private static final String pagenationCountStatementSuffix = "-count";
    private static final String getListStatementSuffix = "Mapper.getList";
    private static final String selectStatementSuffix = "Mapper.findByPriKey";
    private static final String insertStatementSuffix = "Mapper.insert";
    private static final String updateStatementSuffix = "Mapper.update";
    private static final String deleteStatementSuffix = "Mapper.deleteByPriKey";
    private static final String countResult = "CNT";
    private static final String sumPageSuffix = "_SUM_PAGE";



    /**
     * 批量sql单次执行的数量
     */
    private static final int batchExecuteOnceCount = 300;

   /**
    * 
    * 描述:根据主键更新实体信息
    * <p>创建人：jrzhangwei , 2014年7月11日 上午10:16:08</p>
    * @param parameterObject 包含主键的实体信息
    * @return
    */
    public int update(BaseEntity parameterObject) {
    	String statementName = parameterObject.getClass().getSimpleName()+updateStatementSuffix;
        return this.update(statementName, parameterObject);
    }
    
    /**
     * 
     * 描述:根据主键删除记录
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:16:42</p>
     * @param parameterObject 包含主键的实体信息
     * @return
     */
    public int delete(BaseEntity parameterObject) {
    	String statementName = parameterObject.getClass().getSimpleName()+deleteStatementSuffix;
        return super.delete(statementName,parameterObject);
    }
    
    /**
     * 
     * 描述:插入实体信息
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:18:32</p>
     * @param parameterObject 需要插入的实体信息
     * @return
     */
    public Object insert(BaseEntity parameterObject) {
    	String statementName = parameterObject.getClass().getSimpleName()+insertStatementSuffix;
    	return super.insert(statementName, parameterObject);
    }
    
    /**
     * 
     * 描述:根据主键查询记录信息
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:19:01</p>
     * @param parameterObject 包含主键的实体信息
     * @return
     */
	public BaseEntity get(BaseEntity parameterObject) {
		String statementName = parameterObject.getClass().getSimpleName()+selectStatementSuffix;
		return (BaseEntity) this.queryForObject(statementName, parameterObject);
	}
	
	/**
	 * 
	 * 描述:根据条件查询记录
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:19:37</p>
	 * @param parameterObject 包含查询条件的实体信息
	 * @return
	 */
    public List<? extends BaseEntity> getList(BaseEntity parameterObject) {
    	String statementName = parameterObject.getClass().getSimpleName()+getListStatementSuffix;
    	return this.getList(statementName,parameterObject);
    }
    
    /**
     * 
     * 描述:自定义根据条件查询记录
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:20:05</p>
     * @param statementName 查询对应sql
     * @param parameterObject 包含查询条件的实体信息
     * @return
     */
	@SuppressWarnings("unchecked")
	public List<? extends BaseEntity> getList(String statementName, Object parameterObject){
        return this.queryForList(statementName, parameterObject);
    }
	
	 /**
     * 
     * 描述:自定义根据条件查询记录
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:20:05</p>
     * @param statementName 查询对应sql
     * @param parameterObject 包含查询条件的实体信息
     * @return
     */
	@SuppressWarnings("unchecked")
	public List<? extends Object> getList(String statementName){
        return this.queryForList(statementName);
    }
    
	/**
	 * 
	 * 描述:根据条件查询一页记录
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:19:37</p>
	 * @param baseParamDTO 包含查询条件的实体信息
	 * @return
	 */
	public List<? extends BaseEntity> getFirstPagenationList(BasePageDTO baseParamDTO) {
    	if (baseParamDTO == null){
            return null;
        }
    	String statementName = baseParamDTO.getClass().getSimpleName()+pagenationStatementSuffix;
    	return this.getFirstPagenationList(statementName, baseParamDTO);
    }
	
	/**
	 * 
	 * 描述:根据条件查询一页记录
	 * <p>创建人：jrzhangwei , 2014年7月25日 下午12:41:00</p>
	 * @param statementName 分页查询对应sql
	 * @param baseParamDTO 分页查询条件实体信息（其他查询实体信息需继承于该类）
	 * @return
	 */
    @SuppressWarnings("unchecked")
	public List<? extends BaseEntity> getFirstPagenationList(String statementName,BasePageDTO baseParamDTO) {
    	if (baseParamDTO == null){
            return null;
        }
        baseParamDTO.setPageNum(Pagination.DEFAULT_PAGE_NUM);
        if (baseParamDTO.getPageSize() == null || baseParamDTO.getPageSize().intValue() < 1) {
            baseParamDTO.setPageSize(Pagination.DEFAULT_PAGE_SIZE);
        }
        // 计算记录起始值和结束值
        baseParamDTO.setEndIdx(baseParamDTO.getPageSize());
        baseParamDTO.setStartIdx(0);
    	return this.queryForList(statementName, baseParamDTO);
    }
	/**
	 * 
	 * 描述:自定义根据条件分页查询记录
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:21:16</p>
	 * @param statementName 分页查询对应sql
	 * @param baseParamDTO 分页查询条件实体信息（其他查询实体信息需继承于该类）
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Pagination<? extends BaseEntity> getPagenationList(String statementName, BasePageDTO baseParamDTO) {
        if (baseParamDTO == null) {
            return null;
        }
        /**
         * 判断pageNum和pageSize
         */
        if (baseParamDTO.getPageNum() == null || baseParamDTO.getPageNum().intValue() < 1) {
            baseParamDTO.setPageNum(Pagination.DEFAULT_PAGE_NUM);
        }
        if (baseParamDTO.getPageSize() == null || baseParamDTO.getPageSize().intValue() < 1) {
            baseParamDTO.setPageSize(Pagination.DEFAULT_PAGE_SIZE);
        }
        // 计算记录起始值和结束值
        baseParamDTO.setEndIdx(baseParamDTO.getPageSize() * baseParamDTO.getPageNum());
        baseParamDTO.setStartIdx(baseParamDTO.getPageSize() * (baseParamDTO.getPageNum() - 1));
        Map sumResultMap = (Map) this.queryForObject(statementName + pagenationCountStatementSuffix , baseParamDTO);
        Integer totalCount = new Long((Long)sumResultMap.get(countResult)).intValue();
        List resultList = this.queryForList(statementName, baseParamDTO);
        if(sumResultMap != null){
        	if(resultList==null){
        		resultList = new ArrayList();
        	}
    		Map<String,BigDecimal> fieldValueMap = new HashMap<String,BigDecimal>();
        	Iterator<String> sumResultIterator = sumResultMap.keySet().iterator();
        	while(sumResultIterator.hasNext()){
        		String columnName = CurrencyUtils.trimToEmpty(sumResultIterator.next());
        		if(columnName.equals(countResult)){
                	sumResultMap.put(countResult+"_PAGE", resultList.size());
        			continue;
        		}
        		columnName = columnName.substring(0,columnName.length()-4);
        		fieldValueMap.put(ColumnUtil.getcolumnNameToDomainPropertyName(columnName), BigDecimal.ZERO);
        	}
        	try {
        		Class clazz = baseParamDTO.getClass();
        		for(Object object : resultList){
            		Iterator<String> fieldValueIterator = fieldValueMap.keySet().iterator();
        			while(fieldValueIterator.hasNext()){
        				String fieldName  = fieldValueIterator.next();
            			PropertyDescriptor  propertyDescriptor = new PropertyDescriptor(fieldName,clazz);
        				Method method = propertyDescriptor.getReadMethod();
        				BigDecimal sumValueTemp = fieldValueMap.get(fieldName);
        				sumValueTemp = CurrencyUtils.amtAdd(sumValueTemp, new BigDecimal(String.valueOf(method.invoke(object))));
        				fieldValueMap.put(fieldName, sumValueTemp);
            		}
        		}
        		Iterator<String> fieldValueIteratorNew = fieldValueMap.keySet().iterator();
        		while(fieldValueIteratorNew.hasNext()){
        			String fieldName = fieldValueIteratorNew.next();
        			sumResultMap.put(ColumnUtil.getcolumnNameByDomainPropertyName(fieldName)+sumPageSuffix, fieldValueMap.get(fieldName));
        		}
			} catch (Exception e) {
				log.error("当前页数据字段汇总失败",e);
			}
        }
        return new Pagination(baseParamDTO.getPageSize(),baseParamDTO.getPageNum(),totalCount,sumResultMap,resultList);
    }
    
	/**
	 * 
	 * 描述:根据条件分页查询记录
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:23:08</p>
	 * @param baseParamDTO 分页查询条件实体信息（其他查询实体信息需继承于该类）
	 * @return
	 */
	public Pagination<? extends BaseEntity> getPagenationList(BasePageDTO baseParamDTO) {
    	String statementName = baseParamDTO.getClass().getSimpleName()+pagenationStatementSuffix;
    	return this.getPagenationList(statementName, baseParamDTO);
    }

    /**
     * 
     * 描述:根据序号类型名称获取序号信息(该方法原则上不应该放在此处)
     * <p>创建人：jrzhangwei , 2014年7月11日 上午10:24:20</p>
     * @param sequencesName 序号类型名称
     * @return
     */
    @Transactional(propagation=Propagation.REQUIRES_NEW,rollbackFor={Exception.class})
	public Long getNextSeqVal(String sequencesName) {
    	sequencesName = CurrencyUtils.trimToEmpty(sequencesName);
    	if("".equals(sequencesName)){
    		throw new RuntimeException("序列号类型【"+sequencesName+"】不能为空");
    	}
    	SysSequence sequence = new SysSequence();
    	sequence.setSeqType(sequencesName);
    	sequence = (SysSequence) this.get(sequence);
    	sequence.setCurrentValue(sequence.getCurrentValue()==null?0:sequence.getCurrentValue());
    	sequence.setIncrement(sequence.getIncrement()==null?1:sequence.getIncrement());
    	sequence.setCurrentValue(sequence.getCurrentValue()+sequence.getIncrement());
    	int count = this.update(sequence);
    	if(count==1){
    		return sequence.getCurrentValue();
    	}else{
    		throw new RuntimeException("数据并发，产生类型为【"+sequencesName+"】的序列号失败");
    	}
	}

	/**
	 * 
	 * 描述:批量插入实体信息
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:25:45</p>
	 * @param baseEntityList 需要插入的实体列表
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public int batchInsert(final List<? extends BaseEntity> baseEntityList) {
		//每次I/O提交数量
		final int singleNum = batchExecuteOnceCount;
		//sql执行影响数据行数
        int affectedRows = 0;
        try {
        	 if(baseEntityList != null && baseEntityList.size()>0){
            	 final String statementName = baseEntityList.get(0).getClass().getSimpleName()+ insertStatementSuffix;
            	 final int rowSize = baseEntityList.size();//数据总量
             	 log.debug("批量执行插入【"+statementName+"】开始："+System.currentTimeMillis());
        		 this.execute( new SqlMapClientCallback() { 
                     public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { 
                    	 int fromIndex = 0;//起始序号
         				int endIndex = 0;//结束序号
                     	log.debug("批量执行插入【"+statementName+"】开始："+System.currentTimeMillis());
                         for (int i = 0; i==0 || i < Math.ceil((double)(rowSize / singleNum)); i++) {
                         	endIndex = (i + 1) * singleNum;//默认结束序号滚动一页
                         	if(endIndex >= rowSize){//如结束序号大于等于数据总量时，把数据总量赋值给结束序列
                         		endIndex = rowSize;
                         	}
                         	log.debug("批量执行插入【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录开始："+System.currentTimeMillis());
                             executor.startBatch();
                             for (BaseEntity baseEntity : baseEntityList.subList(fromIndex, endIndex)) {
                                 executor.insert(statementName, baseEntity); 
                             }
                             executor.executeBatch();
                             fromIndex = endIndex;
                        	log.debug("批量执行插入【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录结束："+System.currentTimeMillis());
                         }
                        return null;
                     } 
                 }); 
                 log.debug("批量执行插入【"+statementName+"】结束："+System.currentTimeMillis());
            }
		} catch (Exception e) {
			log.error("系统批量插入数据出错：",e);
			throw new RuntimeException(e);
		} 
        return affectedRows;
	}

	/**
	 * 
	 * 描述:批量更新实体信息
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:26:17</p>
	 * @param baseEntityList 需要更新的实体列表
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public 	void batchUpdate(final List<? extends BaseEntity> baseEntityList) {
		//每次I/O提交数量
		final int singleNum = batchExecuteOnceCount;
        try {
        	if(baseEntityList != null && baseEntityList.size()>0){
				final String statementName = baseEntityList.get(0).getClass().getSimpleName() + updateStatementSuffix;
				final int rowSize = baseEntityList.size();//数据总量
            	this.execute( new SqlMapClientCallback() {
                    public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { 
                    	int fromIndex = 0;//起始序号
        				int endIndex = 0;//结束序号
                    	log.debug("批量执行更新【"+statementName+"】开始："+System.currentTimeMillis());
                        for (int i = 0; i==0 || i < Math.ceil((double)(rowSize / singleNum)); i++) {
                        	endIndex = (i + 1) * singleNum;//默认结束序号滚动一页
                        	if(endIndex >= rowSize){//如结束序号大于等于数据总量时，把数据总量赋值给结束序列
                        		endIndex = rowSize;
                        	}
                        	log.debug("批量执行更新【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录开始："+System.currentTimeMillis());
                            executor.startBatch();
                            for (BaseEntity baseEntity : baseEntityList.subList(fromIndex, endIndex)) {
                                executor.update(statementName, baseEntity); 
                            }
                            executor.executeBatch();
                            fromIndex = endIndex;
                       		log.debug("批量执行更新【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录结束："+System.currentTimeMillis());
                        }
                       return null;
                    } 
                }); 
                    log.debug("批量执行更新【"+statementName+"】结束："+System.currentTimeMillis());
                }
		} catch (Exception e) {
			log.error("系统批量更新数据出错：",e);
			throw new RuntimeException(e);
		} 
	}
	
	/**
	 * 
	 * 描述:批量删除实体信息
	 * <p>创建人：jrzhangwei , 2014年7月11日 上午10:27:10</p>
	 * @param baseEntityList 需要删除的实体列表
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public 	void batchDelete(final List<? extends BaseEntity> baseEntityList) {
		//每次I/O提交数量
		final int singleNum = batchExecuteOnceCount;
        try {
        	if(baseEntityList != null && baseEntityList.size()>0){
				final String statementName = baseEntityList.get(0).getClass().getSimpleName() + updateStatementSuffix;
				final int rowSize = baseEntityList.size();//数据总量
            	this.execute( new SqlMapClientCallback() {
                    public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { 
                    	int fromIndex = 0;//起始序号
        				int endIndex = 0;//结束序号
                    	log.debug("批量执行删除【"+statementName+"】开始："+System.currentTimeMillis());
                        for (int i = 0; i==0 || i < Math.ceil((double)(rowSize / singleNum)); i++) {
                        	endIndex = (i + 1) * singleNum;//默认结束序号滚动一页
                        	if(endIndex >= rowSize){//如结束序号大于等于数据总量时，把数据总量赋值给结束序列
                        		endIndex = rowSize;
                        	}
                        	log.debug("批量执行删除【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录开始："+System.currentTimeMillis());
                            executor.startBatch();
                            for (BaseEntity baseEntity : baseEntityList.subList(fromIndex, endIndex)) {
                                executor.delete(statementName, baseEntity); 
                            }
                            executor.executeBatch();
                            fromIndex = endIndex;
                       		log.debug("批量执行删除【"+statementName+"】第【"+(i+1)+"】次共【"+(endIndex-fromIndex)+"】条记录结束："+System.currentTimeMillis());
                        }
                       return null;
                    } 
                }); 
                    log.debug("批量执行删除【"+statementName+"】结束："+System.currentTimeMillis());
                }
		} catch (Exception e) {
			log.error("系统批量删除数据出错：",e);
			throw new RuntimeException(e);
		} 
	}

	/* 
	 * desc:
	 * (non-Javadoc)
	 * @see com.jd.jr.card.dao.base.BaseDao#batchExcute(java.util.List)
	 */
	@Override
	public void batchExecute(List<? extends BaseEntity> baseEntityList) {
		if(CollectionUtils.isNotEmpty(baseEntityList)){
			List<BaseEntity> updateList = new ArrayList<BaseEntity>();
			List<BaseEntity> insertList = new ArrayList<BaseEntity>();
			List<BaseEntity> deleteList = new ArrayList<BaseEntity>();
			for(BaseEntity baseEntity : baseEntityList){
				if(baseEntity.canInsert()){
					insertList.add(baseEntity);
				}else if(baseEntity.canUpdate()){
					updateList.add(baseEntity);
				}else if(baseEntity.canDelete()){
					deleteList.add(baseEntity);
				}else{
					throw new RuntimeException("需要批量操作的实体【"+baseEntity.getClass().getSimpleName()+":"+JSON.toJSONString(baseEntity)+"】未标识操作属性,无法执行批量操作");
				}
			}
			if(CollectionUtils.isNotEmpty(insertList)){
				batchInsert(insertList);
			}
			if(CollectionUtils.isNotEmpty(updateList)){
				batchUpdate(updateList);
			}
			if(CollectionUtils.isNotEmpty(deleteList)){
				batchDelete(deleteList);
			}
		}
	}
	
}